//
// Created by 夏申频 on 16/9/15.
//

#include "JPEGBit.h"

namespace JPEG_NAME_SPACE {

     const int SourceManager::readBufferSize = 2048;
     const int DestinationManager::writeBufferSize = 2048;

    ErrorManager::ErrorManager(JPEG::ImageCodecErrorHandler *h) : errorHandler(h) {
        jpeg_std_error(error_mgr());
    }

    ErrorManager::~ErrorManager() {

    }

    jpeg_error_mgr *ErrorManager::error_mgr() {
        return &(this->pub);
    }

//    void ErrorManager::onError(JPEG::CoderCommon *coder) {
//        //exit(1);
//        longjmp(setjmp_buffer, 1);
//    }

    JPEG::ImageCodecErrorHandler *ErrorManager::getErrorHandler() const {
        return errorHandler;
    }


    //------------source Manger
    SourceManager::~SourceManager() {

    }

    SourceManager::SourceManager(Decoder *decoder, JPEG::ImageInputStream *stream) : readBuf(readBufferSize),
                                                                                     iis(stream) {
        setFlag(true);
        //init(decoder,iis);
    }

    void SourceManager::init_source_handler(j_decompress_ptr cinfo) {
        Decoder *decoder = ( Decoder *) (cinfo->client_data);
        decoder->getSourceManager()->initSource(decoder);
    }

    boolean SourceManager::fill_input_buffer_handler(j_decompress_ptr cinfo) {
        Decoder *decoder =( Decoder *)  (cinfo->client_data);
        return decoder->getSourceManager()->fillInputBuffer(decoder);
    }

    void SourceManager::skip_input_data_handler(j_decompress_ptr cinfo, long num_bytes) {
        Decoder *decoder = ( Decoder *)  (cinfo->client_data);
        decoder->getSourceManager()->skipInputData(decoder, num_bytes);
    }

    void SourceManager::term_source_handler(j_decompress_ptr cinfo) {
        Decoder *decoder = ( Decoder *)  (cinfo->client_data);
        decoder->getSourceManager()->termSource(decoder);
    }

    boolean SourceManager::reSync_to_restart_handler(j_decompress_ptr cinfo, int desired) {
        Decoder *decoder = ( Decoder *) (cinfo->client_data);
        return decoder->getSourceManager()->reSyncToRestart(decoder, desired);
    }


    jpeg_source_mgr *SourceManager::sourceMgr() {
        return pub;
    }

    bool SourceManager::isFlag() const {
        return flag;
    }

    void SourceManager::setFlag(bool flag) {
        SourceManager::flag = flag;
    }

    void SourceManager::init(Decoder *decoder, JPEG::ImageInputStream *stream) {

        j_decompress_ptr cInfo = decoder->getDecompressPtr();
        if (cInfo->src == NULL) {
            cInfo->src = (struct jpeg_source_mgr *)
                    (cInfo->mem->alloc_small)(decoder->getCInfoPtr(), JPOOL_PERMANENT, SIZEOF(jpeg_source_mgr));
            pub = cInfo->src;
            //decoder->setSourceManager(this);
        }
        jpeg_source_mgr *src = cInfo->src;

        src->init_source = SourceManager::init_source_handler;
        src->fill_input_buffer = SourceManager::fill_input_buffer_handler;
        src->skip_input_data = SourceManager::skip_input_data_handler;
        src->resync_to_restart = SourceManager::reSync_to_restart_handler;
        src->term_source = SourceManager::term_source_handler;

        if (isFlag()) {
            src->bytes_in_buffer = 0;
            src->next_input_byte = NULL;
        }
    }

    void SourceManager::initSource(Decoder *decoder) {
        this->startOfFile = true;
    }

    bool SourceManager::fillInputBuffer(Decoder *decoder) {
        size_t nbytes;
        //std::cout<<"start fillInputBuffer"<<std::endl;
        //printf("address of iis:%08x\n",iis);
        //nbytes = JFREAD(test->infile, test->buffer, INPUT_BUF_SIZE);
        int available = iis->available();
        //std::cout<<"after available"<<std::endl;
        if (available <= 0) {
            /* Start the I/O suspension simply by returning false here: */
            return FALSE;
        }
        //std::cout<<"before read"<<std::endl;
        nbytes = iis->read(readBuf, 0, readBufferSize);

        //std::cout<<"after read"<<std::endl;
        if (nbytes <= 0) {
            if (startOfFile)  /* Treat empty input file as fatal error */
                ERREXIT(decoder->getDecompressPtr(), JERR_INPUT_EMPTY);
            WARNMS(decoder->getDecompressPtr(), JWRN_JPEG_EOF);
            /* Insert a fake EOI marker */
            readBuf[0] = (JOCTET) 0xFF;
            readBuf[1] = (JOCTET) JPEG_EOI;
            nbytes = 2;
        }

        pub->next_input_byte = readBuf.data();
        pub->bytes_in_buffer = nbytes;
        startOfFile = FALSE;
        //std::cout<<"before read return"<<std::endl;
        return TRUE;
    }

    void SourceManager::skipInputData(Decoder *decoder, long num_bytes) {

        /* Just a dumb implementation for now.  Could use fseek() except
         * it doesn't work on pipes.  Not clear that being smart is worth
         * any trouble anyway --- large skips are infrequent.
         */
        if (num_bytes > 0) {
            while (num_bytes > (long) pub->bytes_in_buffer) {
                num_bytes -= (long) pub->bytes_in_buffer;
                (void) fillInputBuffer(decoder);
                /* note we assume that fill_input_buffer will never return FALSE,
                 * so suspension need not be handled.
                 */
            }
            pub->next_input_byte += (size_t) num_bytes;
            pub->bytes_in_buffer -= (size_t) num_bytes;
        }

    }

    void SourceManager::termSource(Decoder *decoder) {

    }

    bool SourceManager::reSyncToRestart(Decoder *decoder, int desired) {
        return jpeg_resync_to_restart(decoder->getDecompressPtr(), desired);
    }


    //------------ CoderCommon
    CoderCommon::CoderCommon() : cInfo(NULL), image_buffer(NULL), errorManager(NULL) {
        //initCoder();
    }

    CoderCommon::~CoderCommon() {
        if (image_buffer) {
            delete image_buffer;
            image_buffer = NULL;
        }
        if (errorManager) {
            delete errorManager;
            errorManager = NULL;
        }
    }

    void CoderCommon::error_handler(j_common_ptr cInfo) {
        CoderCommon *coder = static_cast<CoderCommon * > (cInfo->client_data);
        if (coder) {
#ifndef NDEBUG
            (*cInfo->err->output_message)(cInfo);
#endif
            JPEG::ImageCodecErrorHandler *handler = coder->errorManager->getErrorHandler();
            if (handler)
                handler->onError(coder);
        }
    }

    j_common_ptr CoderCommon::getCInfoPtr() {
        return this->cInfo;
    }

    CoderCommon* CoderCommon::initCoder() {
        this->cInfo = this->init_private();
        cInfo->client_data = this;
        row_stride = 0;
        return this;
    }

    void CoderCommon::setErrorHandler(JPEG::ImageCodecErrorHandler *errorHandler) {
        if (this->errorManager)
            delete this->errorManager;

        ErrorManager *em = new ErrorManager(errorHandler);
        this->errorManager = em;
        cInfo->err = em->error_mgr();
        cInfo->err->error_exit = CoderCommon::error_handler;

    }

    void CoderCommon::error(int code) {
        ERREXIT(getCInfoPtr(), code);
    }

    void CoderCommon::warn(int code) {
        WARNMS(getCInfoPtr(), code);
    }

    std::string CoderCommon::lastErrorMsg() {
        int bufferLen = 2048;
        char *buffer = (char *) malloc(bufferLen);
        memset(buffer, 0, bufferLen);
        j_common_ptr c = getCInfoPtr();
        jpeg_error_mgr *err = c->err;
        err->format_message(c, buffer);
        std::string msg(buffer, strnlen(buffer, bufferLen));
        free(buffer);
        return msg;
    }

    int CoderCommon::lastErrorCode() {
        return getCInfoPtr()->err->msg_code;
    }


    //Decoder
    Decoder::~Decoder() {
        close();
#ifdef _WIN32
        if (image_buffer) {
            delete image_buffer;
            image_buffer = NULL;
        }
        if (errorManager) {
            delete errorManager;
            errorManager = NULL;
        }
#else
        JPEG_NAME_SPACE::CoderCommon::~CoderCommon();
#endif
    }

    j_common_ptr Decoder::init_private() {
        jpeg_create_decompress(&dc);
        sourceManager = NULL;
        return (j_common_ptr) &dc;
    }

    int Decoder::readHeader(bool require_image) {
        if (sourceManager == NULL)
            error(JERR_EMPTY_IMAGE);
        return jpeg_read_header(&dc, require_image);
    }

    int Decoder::readScanLines(int lines, JPEG::ImageOutputStream *out) {
        if (sourceManager == NULL)
            error(JERR_EMPTY_IMAGE);
        int r = -1;
        int readLines = 0;
        JSAMPLE *bufferP = (JSAMPLE *) image_buffer->data();
        JSAMPROW row_pointer[1] = {bufferP};
        for (int i = 0; i < lines; i++) {
            if (output_scanline() < output_height()) {
                if ((r = jpeg_read_scanlines(&dc, row_pointer, 1)) == 0) {
                    /* Suspension in jpeg_read_scanlines */
                    // Internals->
                    // StateSuspension = 3;
                    break;
                } else {
                    out->write(*image_buffer, 0, row_stride);
                    readLines++;
                }
            } else {
                break;
            }

        }

        return readLines;
    }

    int Decoder::start() {
        if (sourceManager == NULL)
            error(JERR_EMPTY_IMAGE);
        int v = jpeg_start_decompress(&dc);

        row_stride = output_width() * dc.output_components;
        row_stride *= sizeof(JSAMPLE);

        if (image_buffer) {
            //(dc.mem->free_pool)(getCInfoPtr(), JPOOL_IMAGE);
            delete image_buffer;
        }
//        image_buffer = (dc.mem->alloc_sarray)
//                (getCInfoPtr(), JPOOL_IMAGE, (JDIMENSION) row_stride, 1);
        image_buffer = new JPEG::ImageBuf(row_stride);
        return v;
    }

    int Decoder::finish() {
        if (sourceManager == NULL)
            error(JERR_EMPTY_IMAGE);
        int v = jpeg_finish_decompress(&dc);
        if (image_buffer) {
            delete image_buffer;
            image_buffer = NULL;
        }
        return v;
    }

    void Decoder::close() {
        if (sourceManager) {
            delete sourceManager;
            sourceManager = NULL;
        }
        jpeg_destroy_decompress(&dc);
    }

    j_decompress_ptr Decoder::getDecompressPtr() {
        return &this->dc;
    }

    SourceManager *Decoder::getSourceManager() {
        return this->sourceManager;
    }

    void Decoder::setInput(JPEG::ImageInputStream *input) {
        if (this->sourceManager)
            delete this->sourceManager;

        this->sourceManager = new SourceManager(this, input);
        this->sourceManager->init(this, input);
        if (dc.src == NULL) {
            dc.src = this->sourceManager->sourceMgr();
        }
    }

    void Decoder::setErrorHandler(JPEG::ImageCodecErrorHandler *errorHandler) {
        JPEG_NAME_SPACE::CoderCommon::setErrorHandler(errorHandler);
    }

    void Decoder::error(int code) {
        JPEG_NAME_SPACE::CoderCommon::error(code);
    }

    void Decoder::warn(int code) {
        JPEG_NAME_SPACE::CoderCommon::warn(code);
    }

    std::string Decoder::lastErrorMsg() {
        return JPEG_NAME_SPACE::CoderCommon::lastErrorMsg();
    }

    int Decoder::lastErrorCode() {
        return JPEG_NAME_SPACE::CoderCommon::lastErrorCode();
    }

// DestinationManager
    DestinationManager::DestinationManager(Encoder *encoder, JPEG::ImageOutputStream *o) : ios(o),
                                                                                           writeBuf(writeBufferSize) {

    }

    DestinationManager::~DestinationManager() {

    }

    void DestinationManager::init_destination(j_compress_ptr cInfo) {
        Encoder *encoder = (Encoder *) cInfo->client_data;
        encoder->getDestinationManager()->initDestination(encoder);
    }

    boolean DestinationManager::empty_output_buffer(j_compress_ptr cInfo) {
        Encoder *encoder = (Encoder *) cInfo->client_data;
        return encoder->getDestinationManager()->emptyOutputBuffer(encoder);
    }

    void DestinationManager::term_destination(j_compress_ptr cInfo) {
        Encoder *encoder = (Encoder *) cInfo->client_data;
        encoder->getDestinationManager()->termDestination(encoder);
    }

    void DestinationManager::init(Encoder *encoder, JPEG::ImageOutputStream *outputStream) {

        j_compress_ptr cInfo = encoder->getCompressPtr();
        if (cInfo->dest == NULL) {
            cInfo->dest = (struct jpeg_destination_mgr *)
                    (cInfo->mem->alloc_small)((j_common_ptr) cInfo, JPOOL_PERMANENT, SIZEOF(jpeg_destination_mgr));
        }
        pub = cInfo->dest;
        pub->init_destination = DestinationManager::init_destination;
        pub->empty_output_buffer = DestinationManager::empty_output_buffer;
        pub->term_destination = DestinationManager::term_destination;

    }

    void DestinationManager::initDestination(Encoder *encoder) {
        pub->free_in_buffer = writeBufferSize;
        pub->next_output_byte = writeBuf.data();
    }

    boolean DestinationManager::emptyOutputBuffer(Encoder *encoder) {
        ios->write(writeBuf, 0, writeBufferSize);
        ios->flush();
        pub->free_in_buffer = writeBufferSize;
        pub->next_output_byte = writeBuf.data();
        return TRUE;
    }

    void DestinationManager::termDestination(Encoder *encoder) {
        size_t datacount = writeBufferSize - pub->free_in_buffer;
        ios->write(writeBuf, 0, datacount);
        ios->flush();
    }

    //Encoder
    Encoder::Encoder(){

    }
    Encoder::~Encoder(){
        close();
#ifdef _WIN32
        if (image_buffer) {
            delete image_buffer;
            image_buffer = NULL;
        }
        if (errorManager) {
            delete errorManager;
            errorManager = NULL;
        }
#else
        JPEG_NAME_SPACE::CoderCommon::~CoderCommon();
#endif
    }

    j_compress_ptr  Encoder::getCompressPtr(){
        return &cc;
    }

    j_common_ptr Encoder::init_private() {
        jpeg_create_compress(&cc);
        destinationManager = NULL;
        return (j_common_ptr) &cc;
    }

    void Encoder::setOutput(JPEG::ImageOutputStream *output) {
        if (destinationManager)
            delete destinationManager;
        this->destinationManager = new DestinationManager(this,output);
        this->destinationManager->init(this,output);

    }

    DestinationManager *Encoder::getDestinationManager() {
        return this->destinationManager;
    }

    void Encoder::setErrorHandler(JPEG::ImageCodecErrorHandler *errorHandler){
        JPEG_NAME_SPACE::CoderCommon::setErrorHandler(errorHandler);
    }
    void Encoder::error(int code){
        JPEG_NAME_SPACE::CoderCommon::error(code);
    }
    void Encoder::warn(int code){
        JPEG_NAME_SPACE::CoderCommon::warn(code);
    }
    std::string Encoder::lastErrorMsg(){
        return JPEG_NAME_SPACE::CoderCommon::lastErrorMsg();
    }
    int Encoder::lastErrorCode(){
        return JPEG_NAME_SPACE::CoderCommon::lastErrorCode();
    }

    void Encoder::quality(int v){
        jpeg_set_quality(&cc, v, TRUE );
    }
    void Encoder::lossy(bool l){
        if (!l)
            jpeg_simple_lossless (&cc, 1, 0);
        else
            jpeg_set_quality(&cc, 100, TRUE );
    }

    int Encoder::start(){
        if (destinationManager == NULL)
            error(JERR_FILE_WRITE);
        row_stride = image_width() * cc.input_components;
        row_stride *= sizeof(JSAMPLE);

        if (image_buffer) {
            //(dc.mem->free_pool)(getCInfoPtr(), JPOOL_IMAGE);
            delete image_buffer;
        }
        image_buffer = new JPEG::ImageBuf(row_stride);

        cc.write_JFIF_header = 0;
        jpeg_start_compress(&cc, TRUE);
        return 1;
    }
    int Encoder::finish(){
        if (destinationManager == NULL)
            error(JERR_FILE_WRITE);
        jpeg_finish_compress(&cc);
        if (image_buffer) {
            delete image_buffer;
            image_buffer = NULL;
        }
        return 1;
    }
    void Encoder::setDefault(){
        jpeg_set_defaults(&cc);
    }
    void Encoder::close(){
        if (destinationManager){
            delete destinationManager;
            destinationManager = NULL;
        }
        jpeg_destroy_compress(&cc);
    }

    int Encoder::writeScanLines(int lines,JPEG::ImageInputStream* in){
        int writeLen = 0;
        JSAMPLE *bufferP = (JSAMPLE *) image_buffer->data();
        JSAMPROW row_pointer[1] = {bufferP};

        for(int i=0;i<lines;i++){
            if(cc.next_scanline < cc.image_height){
                in->read(*image_buffer,0,row_stride);
                writeLen+=jpeg_write_scanlines(&cc, row_pointer, 1);
            }else{
                break;
            }
        }

        return writeLen;
    }



    IMPL_ENCODER_GETTER_SETTER(JDIMENSION,image_width);
    IMPL_ENCODER_GETTER_SETTER(JDIMENSION,image_height);
    IMPL_ENCODER_GETTER_SETTER(int,input_components);
    IMPL_ENCODER_GETTER_SETTER(J_COLOR_SPACE,in_color_space);
    IMPL_ENCODER_GETTER_SETTER(double,input_gamma);
    IMPL_ENCODER_GETTER_SETTER(boolean,lossless);
    IMPL_ENCODER_GETTER_SETTER(int,data_precision);

    IMPL_ENCODER_GETTER_SETTER(int,num_components)
    IMPL_ENCODER_GETTER_SETTER(J_COLOR_SPACE ,jpeg_color_space)
    IMPL_ENCODER_GETTER_SETTER(int ,num_scans)
    IMPL_ENCODER_GETTER_SETTER(boolean ,raw_data_in)
    IMPL_ENCODER_GETTER_SETTER(boolean, arith_code)
    IMPL_ENCODER_GETTER_SETTER(boolean, optimize_coding)
    IMPL_ENCODER_GETTER_SETTER(boolean, CCIR601_sampling)
    IMPL_ENCODER_GETTER_SETTER(int, smoothing_factor)

    IMPL_ENCODER_GETTER_SETTER(unsigned int,restart_interval)
    IMPL_ENCODER_GETTER_SETTER(int,restart_in_rows)

    IMPL_ENCODER_GETTER_SETTER(boolean,write_JFIF_header)
    IMPL_ENCODER_GETTER_SETTER(UINT8,JFIF_major_version)
    IMPL_ENCODER_GETTER_SETTER(UINT8,JFIF_minor_version)

    IMPL_ENCODER_GETTER_SETTER(UINT8,density_unit)
    IMPL_ENCODER_GETTER_SETTER(UINT16,X_density)
    IMPL_ENCODER_GETTER_SETTER(UINT16,Y_density)
    IMPL_ENCODER_GETTER_SETTER(boolean,write_Adobe_marker)

    IMPL_ENCODER_GETTER_SETTER(JDIMENSION,next_scanline)
    IMPL_ENCODER_GETTER_SETTER(int,data_unit)
    IMPL_ENCODER_GETTER_SETTER(J_CODEC_PROCESS,process)

    IMPL_ENCODER_GETTER_SETTER(int,max_h_samp_factor)
    IMPL_ENCODER_GETTER_SETTER(int,max_v_samp_factor)

    IMPL_ENCODER_GETTER_SETTER(JDIMENSION,total_iMCU_rows)
    IMPL_ENCODER_GETTER_SETTER(int,comps_in_scan)
    IMPL_ENCODER_GETTER_SETTER(JDIMENSION,MCUs_per_row)
    IMPL_ENCODER_GETTER_SETTER(JDIMENSION,MCU_rows_in_scan)
    IMPL_ENCODER_GETTER_SETTER(int,data_units_in_MCU)
    IMPL_ENCODER_GETTER_SETTER(int ,Ss)
    IMPL_ENCODER_GETTER_SETTER(int,Se);
    IMPL_ENCODER_GETTER_SETTER(int,Ah);
    IMPL_ENCODER_GETTER_SETTER(int,Al);




    IMPL_DECODER_GETTER_SETTER(JDIMENSION ,image_width)
    IMPL_DECODER_GETTER_SETTER(JDIMENSION ,image_height)
    IMPL_DECODER_GETTER_SETTER(int ,num_components)
    IMPL_DECODER_GETTER_SETTER(J_COLOR_SPACE ,jpeg_color_space)
    IMPL_DECODER_GETTER_SETTER(J_COLOR_SPACE ,out_color_space)
    IMPL_DECODER_GETTER_SETTER(unsigned int ,scale_num)
    IMPL_DECODER_GETTER_SETTER(unsigned int,scale_denom)
    IMPL_DECODER_GETTER_SETTER(double ,output_gamma)
    IMPL_DECODER_GETTER_SETTER(boolean ,buffered_image)
    IMPL_DECODER_GETTER_SETTER(boolean ,raw_data_out)
    IMPL_DECODER_GETTER_SETTER(J_DCT_METHOD ,dct_method)
    IMPL_DECODER_GETTER_SETTER(boolean ,do_fancy_upsampling)
    IMPL_DECODER_GETTER_SETTER(boolean ,do_block_smoothing)
    IMPL_DECODER_GETTER_SETTER(boolean ,quantize_colors)
    IMPL_DECODER_GETTER_SETTER(J_DITHER_MODE ,dither_mode)
    IMPL_DECODER_GETTER_SETTER(boolean ,two_pass_quantize)
    IMPL_DECODER_GETTER_SETTER(int ,desired_number_of_colors)
    IMPL_DECODER_GETTER_SETTER(boolean ,enable_1pass_quant)
    IMPL_DECODER_GETTER_SETTER(boolean ,enable_external_quant)
    IMPL_DECODER_GETTER_SETTER(boolean ,enable_2pass_quant)
    IMPL_DECODER_GETTER_SETTER(JDIMENSION ,output_width)
    IMPL_DECODER_GETTER_SETTER(JDIMENSION ,output_height)
    IMPL_DECODER_GETTER_SETTER(int ,out_color_components)
    IMPL_DECODER_GETTER_SETTER(int ,output_components)
    IMPL_DECODER_GETTER_SETTER(int ,rec_outbuf_height)
    IMPL_DECODER_GETTER_SETTER(int ,actual_number_of_colors)
    IMPL_DECODER_GETTER_SETTER(JDIMENSION ,output_scanline)
    IMPL_DECODER_GETTER_SETTER(int ,input_scan_number)
    IMPL_DECODER_GETTER_SETTER(JDIMENSION ,input_iMCU_row)
    IMPL_DECODER_GETTER_SETTER(int ,output_scan_number)
    IMPL_DECODER_GETTER_SETTER(JDIMENSION ,output_iMCU_row)
    IMPL_DECODER_GETTER_SETTER(int ,data_precision)
    IMPL_DECODER_GETTER_SETTER(boolean ,saw_JFIF_marker)
    IMPL_DECODER_GETTER_SETTER(UINT8 ,JFIF_major_version)
    IMPL_DECODER_GETTER_SETTER(UINT8 ,JFIF_minor_version)
    IMPL_DECODER_GETTER_SETTER(UINT8 ,density_unit)
    IMPL_DECODER_GETTER_SETTER(UINT16 ,X_density)
    IMPL_DECODER_GETTER_SETTER(UINT16 ,Y_density)
    IMPL_DECODER_GETTER_SETTER(boolean ,saw_Adobe_marker)
    IMPL_DECODER_GETTER_SETTER(UINT8 ,Adobe_transform)
    IMPL_DECODER_GETTER_SETTER(boolean ,CCIR601_sampling)
    IMPL_DECODER_GETTER_SETTER(int ,data_unit)
    IMPL_DECODER_GETTER_SETTER(J_CODEC_PROCESS ,process)
    IMPL_DECODER_GETTER_SETTER(int ,max_h_samp_factor)
    IMPL_DECODER_GETTER_SETTER(int ,max_v_samp_factor)
    IMPL_DECODER_GETTER_SETTER(int ,min_codec_data_unit)
    IMPL_DECODER_GETTER_SETTER(JDIMENSION ,total_iMCU_rows)
    IMPL_DECODER_GETTER_SETTER(JDIMENSION, MCUs_per_row)
    IMPL_DECODER_GETTER_SETTER(JDIMENSION ,MCU_rows_in_scan)
    IMPL_DECODER_GETTER_SETTER(int ,data_units_in_MCU)
    IMPL_DECODER_GETTER_SETTER(int,Ss)
    IMPL_DECODER_GETTER_SETTER(int ,Se)
    IMPL_DECODER_GETTER_SETTER(int ,Ah)
    IMPL_DECODER_GETTER_SETTER(int,Al)
    IMPL_DECODER_GETTER_SETTER(int,unread_marker)

};